package com.ruoyi.common.core.redis;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

import com.ruoyi.common.config.AppConfig;
import com.ruoyi.common.constant.CacheConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

/**
 * spring redis 工具类
 *
 * @author ruoyi
 **/
@SuppressWarnings(value = { "unchecked", "rawtypes" })
@Component
public class RedisCache
{
    @Autowired
    public RedisTemplate redisTemplate;


    /**
     * 缓存基本的对象ZSet，Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     * @param score 时间戳
     */
    public <T> void setZCacheObject(final String key,final Long score, final T value){
        redisTemplate.opsForZSet().add(key,value,score);
    }

    /**
     * 查询 ZSet 中指定分数范围的元素
     *
     * @param key   缓存的键值
     * @param min   最小时间戳
     * @param max   最大时间戳
     * @return 符合分数范围的元素列表
     */
    public <T> Set<T> getZSetRangeByScore(final String key, final Long min, final Long max) {
        return redisTemplate.opsForZSet().rangeByScore(key, min, max);
    }

    /**
     * 查询 ZSet 中指定key范围的元素==
     *
     * @param key   缓存的键值
     * @param isDesc 是否倒序  true 倒序，false 正序
     * @return 符合key的元素列表
     */
    public <T> Set<T> getZSetRange(final String key,boolean isDesc) {
        if(isDesc){
            return redisTemplate.opsForZSet().range(key, 0, -1);
        }else {
            return redisTemplate.opsForZSet().reverseRange(key, 0, -1);
        }
    }

    /**
     * 查询满足keySet集合的ZSet中，分数在[min, max]范围内的成员
     * @param keySet keySet集合
     * @param min 最小分数
     * @param max 最大分数
     * @return 键值对：key -> 该ZSet中符合条件的成员集合
     */
    public <T> Map<String, Set<T>> rangeByScoreWithSet(Set<String> keySet, double min, double max) {
        Map<String, Set<T>> resultMap = new ConcurrentHashMap<>();
        for (String key : keySet) {
            // 调用rangeByScore获取该ZSet中分数在[min, max]的成员
            Set<T> members = redisTemplate.opsForZSet().rangeByScore(key, min, max);
            if (members != null && !members.isEmpty()) {
                resultMap.put(key, members);
            }
        }
        return resultMap;
    }


    /**
     * 查询所有匹配通配符的ZSet中，分数在[min, max]范围内的成员
     * @param pattern 带通配符的key（如 "aaa:*:bbb"）
     * @param min 最小分数
     * @param max 最大分数
     * @return 键值对：key -> 该ZSet中符合条件的成员集合
     */
    public <T> Map<String, Set<T>> rangeByScoreWithWildcard(String pattern, double min, double max) {
        // 1. 扫描所有匹配的ZSet key  user:sessionId:curMsgs:1:*    user:sessionId:curMsgs:1:eb730ec8edb64eac9b8379702c28bbde
        Set<String> scanKeys = scanKeys(pattern, 1000);

        // 2. 遍历每个key，查询分数范围内的成员
        Map<String, Set<T>> resultMap = new ConcurrentHashMap<>();
        for (String key : scanKeys) {
            // 调用rangeByScore获取该ZSet中分数在[min, max]的成员
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            Set<T> members = redisTemplate.opsForZSet().rangeByScore(key, min, max);
            if (members != null && !members.isEmpty()) {
                resultMap.put(key, members);
            }
        }
        return resultMap;
    }


    /**
     * 扫描匹配通配符的ZSet类型key
     */
    private Set<String> scanZSetKeys(String pattern) {
        Set<String> zsetKeys = new HashSet<>();
        // 使用SCAN命令扫描所有匹配的key
        redisTemplate.execute((RedisCallback<Void>) connection -> {
            try (Cursor<byte[]> cursor = connection.scan(
                    ScanOptions.scanOptions().match(pattern).count(1000).build())) {
                while (cursor.hasNext()) {
                    byte[] keyBytes = cursor.next();
                    String key = (String) redisTemplate.getStringSerializer().deserialize(keyBytes);
                    // 判断key的类型是否为ZSet
                    if (key != null && "zset".equals(redisTemplate.type(key).name())) {
                        zsetKeys.add(key);
                    }
                }
            }
            return null;
        });
        return zsetKeys;
    }




    /**
     * 查询 ZSet 中指定key范围的元素   包括 value  score
     *
     * @param key   缓存的键值
     * @return 符合key的元素列表
     */
    public <T> Set<ZSetOperations.TypedTuple<T>> getZSetRangeWithScores(final String key) {
        // 返回包含 member 和 score 的集合（Entry 的 key 是 member，value 是 score）
        return redisTemplate.opsForZSet().rangeWithScores(key, 0, -1);
    }



    /**
     * 删除 ZSet 中的指定元素
     *
     * @param key   缓存的键值
     * @param value 要删除的元素
     */
    public <T> void removeZSetElement(final String key, final T value) {
        redisTemplate.opsForZSet().remove(key, value);
    }

    /**
     * 批量删除 ZSet 中的指定元素
     *
     * @param keySet   缓存的键值
     */
    public <T> Long removeBatchKeys(final Set<String> keySet) {
        Long count = 0L;
        if(CollectionUtils.isEmpty(keySet)){
            return count;
        }
        for (String scanKey : keySet){
            redisTemplate.delete(scanKey);
            count++;
        }
        return count;
    }

    /**
     * 批量设置缓存对象  与 redis 通信次数为1
     * @param dataList
     * @param idGetter
     * @param <T>
     */
    public <T> void setBatchObject(Set<T> dataList, Function<T, String> idGetter) {
        // 空列表直接返回，避免空指针和无效操作
        if (dataList == null || dataList.isEmpty()) {
            return;
        }

        // 获取Redis连接
        RedisConnection connection = Objects.requireNonNull(redisTemplate.getConnectionFactory()).getConnection();
        try {
            // 开启管道
            connection.openPipeline();

            Expiration expiration = Expiration.seconds(AppConfig.CACHE_TTL * 60L);
            // 批量添加所有键值对到管道
            for (T data : dataList) {
                // 通过传入的函数获取ID
                String key = idGetter.apply(data);

                // 将对象序列化为Redis可存储的字节数组
                byte[] keyBytes = redisTemplate.getKeySerializer().serialize(key);
                byte[] valueBytes = redisTemplate.getValueSerializer().serialize(data);

                // 添加SET命令到管道
                if(valueBytes != null && keyBytes != null){
                    connection.set(keyBytes, valueBytes, expiration, RedisStringCommands.SetOption.UPSERT);
                }
            }

            // 执行管道中的所有命令
            connection.closePipeline();
        } finally {
            // 释放连接
            RedisConnectionUtils.releaseConnection(connection, redisTemplate.getConnectionFactory(), false);
        }
    }



    /**
     * 缓存基本的对象，Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     */
    public <T> void setCacheObject(final String key, final T value){
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 缓存基本的对象，Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     * @param timeout 时间
     * @param timeUnit 时间颗粒度
     */
    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit){
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }

    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @return true=设置成功；false=设置失败
     */
    public boolean expire(final String key, final long timeout){
        return expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @param unit 时间单位
     * @return true=设置成功；false=设置失败
     */
    public boolean expire(final String key, final long timeout, final TimeUnit unit){
        return redisTemplate.expire(key, timeout, unit);
    }

    /**
     * 获取有效时间
     *
     * @param key Redis键
     * @return 有效时间
     */
    public long getExpire(final String key){
        return redisTemplate.getExpire(key);
    }

    /**
     * 判断 key是否存在
     *
     * @param key 键
     * @return true 存在 false不存在
     */
    public Boolean hasKey(String key){
        return redisTemplate.hasKey(key);
    }

    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T getCacheObject(final String key){
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }

    /**
     * 删除单个对象
     *
     * @param key
     */
    public boolean deleteObject(final String key){
        return Boolean.TRUE.equals(redisTemplate.delete(key));
    }

    /**
     * 删除集合对象
     *
     * @param collection 多个对象
     * @return
     */
    public boolean deleteObject(final Collection collection){
        return redisTemplate.delete(collection) > 0;
    }

    /**
     * 缓存List数据
     *
     * @param key 缓存的键值
     * @param dataList 待缓存的List数据
     * @return 缓存的对象
     */
    public <T> long setCacheList(final String key, final List<T> dataList){
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }

    /**
     * 获得缓存的list对象
     *
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    public <T> List<T> getCacheList(final String key){
        return redisTemplate.opsForList().range(key, 0, -1);
    }

    /**
     * 缓存Set
     *
     * @param key 缓存键值
     * @param dataSet 缓存的数据
     * @return 缓存数据的对象
     */
    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet){
        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
        Iterator<T> it = dataSet.iterator();
        while (it.hasNext())
        {
            setOperation.add(it.next());
        }
        return setOperation;
    }

    /**
     * 通过模糊key，获得缓存的set
     *
     * @param key
     * @return
     */
    public <T> Set<T> getCacheSetByMatchKey(final String key){
        Set<String> scanKeys = scanKeys(key, 1000);
        return getTs(new ArrayList<>(scanKeys));
    }

    private <T> Set<T> getTs(List<String> scanKeys) {
        Set<T> cacheSet = new HashSet<>();
        for (String scanKey : scanKeys) {
            T cacheObject = getCacheObject(scanKey);
            if(cacheObject != null){
                cacheSet.add(cacheObject);
            }
        }
        return cacheSet;
    }


    /**
     * 通过key，获得缓存的set
     *
     * @param keys
     * @return
     */
    public <T> Set<T> getCacheSetByKeys(final List<String> keys){
        return getTs(keys);
    }


    /**
     * 获得缓存的set
     *
     * @param key
     * @return
     */
    public <T> Set<T> getCacheSet(final String key){
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 缓存Map
     *
     * @param key
     * @param dataMap
     */
    public <T> void setCacheMap(final String key, final Map<String, T> dataMap){
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }
    }

    public <T> void setCacheMap(final String key, final Map<String, T> dataMap, long timeout, TimeUnit unit) {
        if (dataMap != null && !dataMap.isEmpty()) { // 增加非空判断，避免无效操作
            redisTemplate.opsForHash().putAll(key, dataMap);
            // 设置过期时间：key在指定时间后自动失效
            redisTemplate.expire(key, timeout, unit);
        }
    }

    /**
     * 获得缓存的Map
     *
     * @param key
     * @return
     */
    public <T> Map<String, T> getCacheMap(final String key){
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 往Hash中存入数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @param value 值
     */
    public <T> void setCacheMapValue(final String key, final String hKey, final T value){
        redisTemplate.opsForHash().put(key, hKey, value);
    }

    /**
     * 获取Hash中的数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    public <T> T getCacheMapValue(final String key, final String hKey){
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(key, hKey);
    }

    /**
     * 获取多个Hash中的数据
     *
     * @param key Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys){
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }

    /**
     * 删除Hash中的某条数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @return 是否成功
     */
    public boolean deleteCacheMapValue(final String key, final String hKey){
        return redisTemplate.opsForHash().delete(key, hKey) > 0;
    }

    /**
     * 获得缓存的基本对象列表
     *
     * @param pattern 字符串前缀
     * @return 对象列表
     */
    public Collection<String> keys(final String pattern){
        return redisTemplate.keys(pattern);
    }

    /**
     * 扫描匹配的Redis键（支持模糊匹配）
     * @param pattern 匹配模式（如 "aaa:*:bbb"）
     * @param count 每次扫描的数量（建议100-1000）
     * @return 所有匹配的键集合
     */
    public <T> Set<T> scanKeys(String pattern, int count) {
        // TODO 待完善  类型转化可能有问题
        Set<String> keys = new HashSet<>();
        // 设置 Key 的序列化器为 StringRedisSerializer，确保 Key 能正确反序列化
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        // 显式指定使用 RedisCallback 类型的 execute 方法
        return (Set<T>) redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
            Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions()
                    .match(pattern) // 设置匹配的模式
                    .count(count)   // 设置每次扫描的数量建议值
                    .build());
            while (cursor.hasNext()) {
                // 将 byte[] 类型的 Key 反序列化为 String 并加入结果集
                String key = new String(cursor.next());
                keys.add(key);
            }
            // 关闭游标，释放资源
            cursor.close();
            return keys;
        });
    }


    public void increment(String versionKey, int i) {
        redisTemplate.opsForValue().increment(versionKey, i);
    }

//    public String getCurValidMsgKey(String messageVersion) {
//        if(messageVersion == null){
//            messageVersion = "1";  // 第一次查询的时候，默认给1
//            setCacheObject(CacheConstants.MSG_VERSION, messageVersion);
//        }
//        return "1".equals(messageVersion) ? CacheConstants.MSG_V1_KEY : CacheConstants.MSG_V2_KEY;
//    }

}
